package com.supermap;

import com.alibaba.fastjson.JSONObject;
import com.supermap.ipfs.IpfsClient;
import com.supermap.ipfs.IpfsConnection;
import com.supermap.sqlite.SqliteConnection;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.logging.Logger;

import static com.supermap.server.ServerHandler.getTime;

/**
 * @author liqs
 * @version 1.0
 * @date 2021/10/12 9:49
 */
public class Utils {

    private static Logger logger = Logger.getLogger(Utils.class.toString());
    public static String backupFileName = "backup.txt";
    public static String backupFileName2 = "backup2.txt";

    public static void synchronizeIpfsData() throws IOException {

        long startTime = System.currentTimeMillis();
        // 首先判断当前文件的行数 然后根据时间和行数确定将其复制为一个新的文件
        while (true) {
            long lineNumber = getLineNumber(backupFileName);
            long time = System.currentTimeMillis() - startTime;
            // 当累计数据超过100 或者累计时间 2秒就开始同步数据
            if (lineNumber > 100 || time > 2000) {
                copyFile();
                // 对新文件进行 ipfs 的数据同步 同步完数据之后将新文件删除  同时根据行数删除原始文件中对应的内容
                readFileByLine();
                // 删除备份文件
                new File(backupFileName2).delete();
                // 删除原文件中的某些内容
                readAndRemoveFirstLines(new File(backupFileName), lineNumber);
                // 重新置零时间
                startTime = System.currentTimeMillis();
            }
        }
    }

    /**
     * 按行读取文件
     * @throws IOException
     */
    public static void readFileByLine() throws IOException {
        FileInputStream fis = new FileInputStream(backupFileName2);
        RandomAccessFile raf = new RandomAccessFile(new File(backupFileName2),"r");
        String s ;
        while((s = raf.readLine()) != null){
            synchronizeDataToIpfs(s.split("\\:"));
        }
        raf.close();
        fis.close();
    }

    /**
     * 将数据同步到 ipfs 中
     * @param split
     */
    private static void synchronizeDataToIpfs(String[] split) {
        if (split.length != 0) {
            String ipfsIp = split[0];
            String ipfsPort = split[1];
            String hashId = split[2];
            IpfsClient ipfsClient = IpfsConnection.getIpfsClient(ipfsIp, ipfsPort);
            ipfsClient.downloadBytes(hashId);
//            String msg = getTime() + " com.suermap.ServerHandler\n" +
//                    "INFO: upload file to IPFS [" + ipfsIp + ":" + ipfsPort + " ] success, file hash id is : " + hashId;
//            System.out.println(msg);
            logger.info("upload file to IPFS [" + ipfsIp + ":" + ipfsPort + " ] success, file hash id is : " + hashId);
        }
    }

    /**
     * 获取文件行数
     * @param filePath
     * @return
     */
    public static long getLineNumber(String filePath) {
        File file = new File(filePath);
        if (file.exists()) {
            try {
                FileReader fileReader = new FileReader(file);
                LineNumberReader lineNumberReader = new LineNumberReader(fileReader);
                lineNumberReader.skip(Long.MAX_VALUE);
                long lines = lineNumberReader.getLineNumber() + 1;
                fileReader.close();
                lineNumberReader.close();
                return lines;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return 0;
    }

    /**
     * 将 hashid 和 ipfs 信息存入到一张表中 防止宕机时仍然可用
     */
    public static void saveHashIdAndIpfsInfo(String hashId, String ipfsIp, String ipfsPort) {
        File file = new File(backupFileName);
        if (!file.exists()) {
            try {
                file.createNewFile();
            } catch (Exception e) {
                System.out.println(e.toString());
            }
        }
        String str = ipfsIp + ":" + ipfsPort + ":" + hashId + "\r\n";
        try {
            Files.write(Paths.get(backupFileName), str.getBytes(StandardCharsets.UTF_8), StandardOpenOption.APPEND);
        } catch (Exception e) {
            System.out.println(e.toString());
        }
    }

    /**
     * 分为两步  首先存入到 sqlite 数据库
     * 然后 ipfs 拉取数据
     * 数据拉取完之后 在
     */
    public static void delHashIdAndIpfsInfo(String hashId, String ipfsIp, String ipfsPort) {
        Connection sqliteConnection = null;
        Statement stat = null;
        String uuid = UUID.randomUUID().toString();
        String value = ipfsIp + ":" + ipfsPort + ":" + hashId;
        try {
            // 存入 sqlite 中
            sqliteConnection = SqliteConnection.getSqliteConnection();
            stat = sqliteConnection.createStatement();
            stat.executeUpdate("insert into tbl1 values('" + uuid + "', '" + value + "');");

            // ipfs 拉取数据
            IpfsClient ipfsClient = IpfsConnection.getIpfsClient(ipfsIp, ipfsPort);
            ipfsClient.downloadBytes(hashId);
            logger.info("upload file to IPFS [" + ipfsIp + ":" + ipfsPort + " ] success, file hash id is : " + hashId);
            // 从 sqlite中删除

            // delete
            stat.executeUpdate("delete from tbl1 where uuid='" + uuid + "'");
        } catch (Exception e) {
            logger.info(e.getMessage());
        } finally {
            try {
                stat.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

    }

    /**
     * 复制文件
     * @throws IOException
     */
    public static void copyFile() throws IOException {
        File file = new File(backupFileName);
        File file2 = new File(backupFileName2);
        if (file.exists()) {
            if (file2.exists()) {
                file2.delete();
            }
            Files.copy(file.toPath(), file2.toPath());
        } else {
            file.createNewFile();
            Files.copy(file.toPath(), file2.toPath());
        }
    }

    /**
     * 删除文件的前几行  不使用临时文件
     * @param file
     * @param lineNum
     * @return
     * @throws IOException
     */
    private static List<String> readAndRemoveFirstLines(File file, Long lineNum) throws IOException{
        List<String> strList = new ArrayList<String>();
        RandomAccessFile raf = null;
        try{
            raf = new RandomAccessFile(file, "rw");
            //Initial write position
            long writePosition = raf.getFilePointer();
            for (int i = 0 ; i < lineNum ; i++){
                String line = raf.readLine();
                if(line == null){
                    break;
                }
                strList.add(line);
            }
            // Shift the next lines upwards.
            long readPosition = raf.getFilePointer();

            byte[] buff = new byte[1024];
            int n;
            while (-1 != (n = raf.read(buff))) {
                raf.seek(writePosition);
                raf.write(buff, 0, n);
                readPosition += n;
                writePosition += n;
                raf.seek(readPosition);
            }
            raf.setLength(writePosition);
        } catch(IOException e){
            System.out.println("readAndRemoveFirstLines error" + e.toString());
            throw e;
        } finally{
            try{
                if(raf != null){
                    raf.close();
                }
            }catch(IOException e){
                System.out.println("close RandomAccessFile error" + e.toString());
                throw e;
            }
        }
        return strList;
    }

}
